5.16. Функции
Функции
В языке программирования COBOL понятие «функция» в привычном для современных языков смысле — как именованная единица кода, принимающая параметры и возвращающая значение — отсутствует в ранних версиях стандарта. Вместо этого структурной и логической основой модульности выступают параграфы (PARAGRAPH) и секции (SECTION). Эти элементы выполняют роль процедур или подпрограмм, организуя последовательности операторов в логически завершённые блоки, которые можно многократно использовать в рамках программы.
Параграфы как основа повторного использования кода
Параграф в COBOL представляет собой именованный блок инструкций, начинающийся с уникального имени, за которым следует точка. Имя параграфа должно быть уникальным в пределах процедуры (Procedure Division), где он определён. После имени располагается одна или несколько исполняемых инструкций, каждая из которых завершается точкой или входит в состав многострочной конструкции, корректно оформленной согласно синтаксису COBOL.
Пример простого параграфа:
DISPLAY-MESSAGE.
DISPLAY "Добро пожаловать в систему обработки данных".
Этот параграф не принимает аргументов и не возвращает результат, но инкапсулирует конкретное действие — вывод сообщения на экран. Такой подход позволяет избежать дублирования кода и повышает читаемость программы, особенно в контексте больших систем обработки транзакций, где одни и те же операции повторяются многократно.
Вызов параграфа осуществляется с помощью управляющего глагола PERFORM. Этот глагол указывает интерпретатору или компилятору перейти к указанному параграфу, выполнить все содержащиеся в нём инструкции и вернуться к следующему оператору после PERFORM. Таким образом, PERFORM реализует механизм передачи управления, аналогичный вызову подпрограммы.
Пример вызова:
PERFORM DISPLAY-MESSAGE.
Такой вызов обеспечивает линейное выполнение логики, заключённой в параграфе, без необходимости копировать её содержимое в нескольких местах программы.
Секции как крупные логические блоки
Секция (SECTION) — это более крупная структурная единица по сравнению с параграфом. Секция может содержать один или несколько параграфов и используется для группировки связанных по смыслу операций. Например, секция может представлять этап обработки заказа, включающий проверку наличия товара, расчёт стоимости и формирование подтверждения.
Определение секции выглядит так:
ORDER-PROCESSING SECTION.
VALIDATE-ITEM.
...
CALCULATE-TOTAL.
...
CONFIRM-ORDER.
...
Вызов всей секции также осуществляется через PERFORM:
PERFORM ORDER-PROCESSING.
В этом случае последовательно выполняются все параграфы, входящие в секцию, в порядке их объявления. Секции позволяют создавать иерархическую структуру программы, разделяя её на функциональные модули высокого уровня.
Передача данных между параграфами
Хотя классический COBOL не предоставляет механизма передачи параметров в параграфы напрямую, как это реализовано в функциях других языков, взаимодействие между блоками кода достигается через общие переменные, объявленные в разделе Data Division. Все параграфы в пределах одной программы имеют доступ к этим данным, что позволяет им обмениваться информацией.
Например, один параграф может записать значение в переменную TOTAL-AMOUNT, а другой — прочитать это значение и использовать его для дальнейших вычислений. Такой подход требует дисциплины при проектировании структуры данных и чёткого документирования ролей каждой переменной, чтобы избежать побочных эффектов и трудноуловимых ошибок.
В более поздних стандартах COBOL, начиная с COBOL 2002, были введены настоящие внешние функции (external functions) и внутренние методы, поддерживающие передачу параметров и возврат значений. Однако эти возможности редко используются в унаследованных системах, где преобладает классическая структура на основе параграфов и секций.
Многократное выполнение через PERFORM
Глагол PERFORM обладает расширенными возможностями, выходящими за рамки простого однократного вызова. Он поддерживает циклическое выполнение параграфа заданное количество раз, выполнение до тех пор, пока выполняется условие, или обработку диапазона параграфов.
Примеры:
-
Выполнение 10 раз:
PERFORM PROCESS-RECORD 10 TIMES. -
Выполнение до тех пор, пока условие истинно:
PERFORM UNTIL END-OF-FILE = 'YES'
READ NEXT RECORD
PERFORM VALIDATE-RECORD
END-PERFORM. -
Выполнение диапазона:
PERFORM START-ROUTINE THRU END-ROUTINE.
Эти конструкции делают PERFORM мощным инструментом управления потоком выполнения, сочетающим в себе черты вызова подпрограммы и организации циклов.
Отсутствие возврата значения в классическом COBOL
В ранних версиях COBOL параграфы и секции не возвращают значения. Результат работы такого блока фиксируется через изменение состояния глобальных переменных или файлов. Это соответствует процедурному стилю программирования, где акцент делается на последовательном изменении данных, а не на вычислении и возврате результатов.
Такой подход хорошо согласуется с задачами, для которых COBOL изначально создавался: массовая обработка записей, работа с файлами фиксированной длины, генерация отчётов. В этих сценариях важна не столько композиция функций, сколько надёжность, предсказуемость и чёткая последовательность шагов.
Появление настоящих функций
Начиная со стандарта COBOL 2002, язык получил поддержку пользовательских функций, объявляемых с помощью ключевого слова FUNCTION-ID и способных возвращать значение через оператор RETURNING. Такие функции могут принимать параметры, иметь локальные переменные и использоваться в выражениях, как в языках C или Java.
Пример:
FUNCTION-ID. CALCULATE-TAX.
DATA DIVISION.
LINKAGE SECTION.
01 INPUT-AMOUNT PIC 9(5)V99.
01 TAX-RATE PIC 9(3)V99.
PROCEDURE DIVISION USING INPUT-AMOUNT TAX-RATE RETURNING RESULT.
COMPUTE RESULT = INPUT-AMOUNT * TAX-RATE / 100.
GOBACK.
END FUNCTION CALCULATE-TAX.
Эта функция может вызываться внутри выражения:
COMPUTE FINAL-PRICE = BASE-PRICE + FUNCTION CALCULATE-TAX(BASE-PRICE, 20).
Тем не менее, внедрение таких функций в существующие системы происходит медленно. Большинство производственных COBOL-приложений по-прежнему полагаются на проверенную десятилетиями модель параграфов и секций.
Вложенность и организация параграфов в иерархию
В COBOL параграфы и секции формируют древовидную структуру программы, где каждый уровень вызова добавляет новую вложенность в логику выполнения. Такая организация особенно важна в крупных приложениях, где обработка данных разбита на множество этапов: чтение входных файлов, валидация записей, трансформация данных, запись результатов, формирование отчётов. Каждый из этих этапов может быть представлен отдельной секцией, внутри которой расположены специализированные параграфы.
Например, секция INPUT-PROCESSING может содержать параграфы OPEN-INPUT-FILE, READ-NEXT-RECORD, CHECK-FOR-END-OF-FILE. Секция OUTPUT-GENERATION — параграфы FORMAT-LINE, WRITE-TO-REPORT, CLOSE-OUTPUT-FILE. Такая декомпозиция позволяет разработчику мыслить на уровне бизнес-операций, а не отдельных машинных инструкций.
Параграфы могут вызывать другие параграфы, создавая цепочки выполнения. Это не рекурсия в классическом понимании (рекурсивные вызовы в COBOL ограничены или требуют явного разрешения), но последовательная передача управления от одного блока к другому. Такой подход обеспечивает чёткое разделение ответственности: один параграф отвечает за чтение, другой — за проверку, третий — за запись.
Управление потоком выполнения через PERFORM
Глагол PERFORM является центральным элементом управления выполнением в процедурной части COBOL-программы. Он не только вызывает параграфы, но и определяет контекст их выполнения. Существует несколько форм записи PERFORM, каждая из которых поддерживает определённый сценарий:
-
Простой вызов:
PERFORM paragraph-name.
Выполняет указанный параграф один раз и возвращается к следующему оператору. -
Выполнение диапазона:
PERFORM paragraph-A THRU paragraph-Z.
Выполняет все параграфы отparagraph-Aдоparagraph-Zвключительно, в порядке их объявления в исходном коде. Эта форма удобна для группировки связанных операций без необходимости оборачивать их в секцию. -
Циклическое выполнение:
PERFORM paragraph-name n TIMES.
Повторяет выполнение указанного параграфа заданное количество раз. Числоnможет быть литералом или переменной. -
Условное выполнение (UNTIL):
PERFORM UNTIL condition
statements
END-PERFORM.Выполняет блок инструкций до тех пор, пока условие не станет истинным. Условие проверяется до каждой итерации (предварительная проверка).
-
Условное выполнение (VARYING):
PERFORM VARYING counter FROM start-value BY increment
UNTIL termination-condition
statements
END-PERFORM.Аналог цикла
forв других языках. Переменная-счётчик автоматически инициализируется, изменяется и проверяется на каждом шаге.
Эти конструкции делают PERFORM универсальным инструментом, сочетающим в себе функции вызова подпрограммы, цикла и условного блока. При этом вся логика остаётся линейной и предсказуемой — важное качество для систем, где ошибки могут привести к финансовым потерям или нарушению регуляторных требований.
Отладка и читаемость параграфов
Одним из преимуществ параграфной структуры является её прозрачность для отладки. Поскольку каждый параграф имеет уникальное имя, трассировка выполнения программы сводится к последовательному отслеживанию переходов между этими именами. Многие COBOL-среды разработки и отладчики отображают текущий активный параграф, что упрощает локализацию ошибок.
Кроме того, имена параграфов часто отражают бизнес-смысл операции: VALIDATE-CUSTOMER-ID, CALCULATE-MONTHLY-BALANCE, GENERATE-INVOICE-HEADER. Это делает код самодокументируемым, особенно для аналитиков и специалистов по поддержке, которые могут не быть профессиональными программистами, но обязаны понимать логику обработки.
Совместимость и поддержка унаследованного кода
Большинство действующих COBOL-систем были написаны десятки лет назад и поддерживаются в неизменном виде благодаря своей стабильности. Переход на современные функции (например, FUNCTION-ID) требует перекомпиляции, тестирования и, зачастую, изменения архитектуры данных. Поэтому организации предпочитают сохранять существующую модель параграфов, даже если компилятор поддерживает новые возможности.
Это создаёт ситуацию, в которой «функции» в COBOL — это не технический термин, а прагматический паттерн: повторно используемый, именованный блок логики, вызываемый через PERFORM. Такой паттерн доказал свою жизнеспособность в условиях эксплуатации критически важных банковских, страховых и государственных систем, где отказ недопустим.